﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MSWMDMLib;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices;
using System.Collections;
using FireZone.DataStructure;
using System.Threading;
using FireZone.NativeWMDMInteface;
using FireZone.WMDMHelper;
using FireZone.OperationLogger;
using System.Threading.Tasks.Schedulers;
using System.IO;

namespace FireZone
{
    internal static class FileOperationExtensions
    {
        public static List<Task<MTPStorageItem>> MTPInsertFilesToPredefinedStorageAsync(this MTPStorageItem grandParentStorageItem, MTPStorageItem rootStorageItem, CancellationToken cancellationToken, TaskScheduler taskScheduler, params string[] filenames)
        {
            if (grandParentStorageItem == null) throw new ArgumentNullException("grandParentStorageItem");
            return filenames.Where((p)=>
            { 
                return grandParentStorageItem.predefineStorageList.Any((q) => 
                { 
                    return (q.FileExtensions != null)?q.FileExtensions.ContainsKey(System.IO.Path.GetExtension(p)):false;
                }); 
            })
            .Select((p) => 
            { 
                return grandParentStorageItem.predefineStorageList.First((q) => 
                { 
                    return q.FileExtensions.ContainsKey(System.IO.Path.GetExtension(p));
                }).StorageItem.Value.MTPInsertSingleFileAsync(rootStorageItem, cancellationToken, taskScheduler, false, p); 
            }).ToList();            
        }

        public static List<Task<MTPStorageItem>> MTPInsertFilesAsync(this MTPStorageItem parentStorageItem, MTPStorageItem rootStorageItem, CancellationToken cancellationToken, TaskScheduler taskScheduler, bool createAsEmpty, params string[] filenames)
        {
            return filenames.Select((fileName) =>
            {
                return parentStorageItem.MTPInsertSingleFileAsync(rootStorageItem, cancellationToken, taskScheduler, createAsEmpty, fileName);
            }).ToList();
        }

        public static List<MTPStorageItem> MTPInsertFilesSync(this MTPStorageItem parentStorageItem, MTPStorageItem rootStorageItem, bool createAsEmpty, params string[] filenames)
        {
            return filenames.Select<string, MTPStorageItem>((fileName) => { 
                OperationLoggerItem loggeritem = new OperationLoggerItem(){ DisplayName = fileName, OperationType = OperationType.Insert, IsQueued = false, IsCompleted = false, IsFailed = false, };
                loggeritem.AddLoggerItem();
                return parentStorageItem.MTPInsertSingleFileSync(rootStorageItem, CancellationToken.None, createAsEmpty, fileName, loggeritem); 
            }).ToList();
        }

        public static MTPStorageItem MTPInsertSingleFileSync(this MTPStorageItem parentStorageItem, MTPStorageItem rootStorageItem, CancellationToken cancellationToken, bool createAsEmpty, string fileFullPath, OperationLoggerItem loggeritem = null)
        {
            if (loggeritem == null) loggeritem = new OperationLoggerItem() { OperationType = OperationType.Insert, DisplayName = fileFullPath, IsQueued = false, IsFailed = false, IsCompleted = false, };
            IWMDMMetaData wmdmMetadata = null;
            try
            {
                WMDMStorage wmdmStg = null;
                MTPStorageItem newMTPStorageItem = null;
                parentStorageItem.StorageItem.CreateEmptyMetadataObject(out wmdmMetadata);
                wmdmMetadata.FillFileMetadataFromParsingName(fileFullPath);
                IWMDMStorageControl3 wmdmStgCtlV3 = (IWMDMStorageControl3)parentStorageItem.StorageItem;

                WMDMStorage wmdmOldStorage;
                parentStorageItem.StorageItem.GetStorage(Path.GetFileName(fileFullPath), out wmdmOldStorage);
                if(wmdmOldStorage != null)
                {
                    ((IWMDMStorageControl)wmdmOldStorage).Delete((uint)WMDM_MODE.WMDM_MODE_BLOCK, null);
                }

                wmdmStgCtlV3.Insert3((uint)WMDM_MODE.WMDM_MODE_BLOCK | (uint)WMDM_MODE.WMDM_MODE_PROGRESS | (uint)WMDM_MODE.WMDM_STORAGECONTROL_INSERTINTO | (uint)WMDM_MODE.WMDM_CONTENT_OPERATIONINTERFACE | (uint)WMDM_STORAGE_ATTR.WMDM_FILE_CREATE_OVERWRITE,
                    WMDM_STORAGE_ATTR.WMDM_FILE_ATTR_FILE,
                    null,
                    null,
                    new WMDMOperationImplHelper(fileFullPath, cancellationToken, createAsEmpty),
                    new WMDMProgressImplHelper(loggeritem),
                    wmdmMetadata,
                    null,
                    out wmdmStg);
                rootStorageItem.SafeInvalidateStorageGlobal();

                if (wmdmStg != null)
                {
                    newMTPStorageItem = new MTPStorageItem() { StorageItem = (IWMDMStorage3)wmdmStg, StorageName = System.IO.Path.GetFileName(fileFullPath) };
                    (parentStorageItem.ChildStorages as ObservableCollection<MTPStorageItem>).AddOrUpdateAsync(newMTPStorageItem);                    
                }
                if (rootStorageItem != null) rootStorageItem.SafeInvalidateStorageGlobal();
                return newMTPStorageItem;
            }
            finally
            {
                if (wmdmMetadata != null) Marshal.ReleaseComObject(wmdmMetadata);
            }
        }
        public static Task<MTPStorageItem> MTPInsertSingleFileAsync(this MTPStorageItem parentStorageItem, MTPStorageItem rootStorageItem, CancellationToken cancellationToken, TaskScheduler taskScheduler, bool createAsEmpty, string fileFullPath)
        {
            if (parentStorageItem == null) throw new ArgumentNullException("parentStorageItem");
            OperationLoggerItem loggeritem = new OperationLoggerItem() { DisplayName = fileFullPath, IsQueued = true, IsCanceled = false, IsCompleted = false, OperationType = OperationType.Insert };
            loggeritem.AddLoggerItem();
            var cancellationTokenRegistration = cancellationToken.Register(CancellationTokenRegisterCallback, new WeakReference(loggeritem));
            return Task<MTPStorageItem>.Factory.StartNew(new Func<MTPStorageItem>(() =>
            {
                try
                {
                    loggeritem.IsQueued = false;
                    return parentStorageItem.MTPInsertSingleFileSync(rootStorageItem, cancellationToken, createAsEmpty, fileFullPath, loggeritem);
                }
                catch(COMException e)
                {
                    if (e.ErrorCode == unchecked((int)WMDM_ERROR.WMDM_E_USER_CANCELLED))
                    {
                        loggeritem.IsCanceled = true;
                    }
                    else
                    {
                        loggeritem.IsFailed = true;
                        loggeritem.DisplayMessage = e.Message;
                    }
                }
                finally
                {
                    loggeritem.HasProgress = false;
                    loggeritem.IsCompleted = true;
                    cancellationTokenRegistration.Dispose();
                }
                return null;
            }), cancellationToken, TaskCreationOptions.None, taskScheduler);
        }

        private static void CancellationTokenRegisterCallback(object objState)
        {
            OperationLoggerItem item = (objState as WeakReference).Target as OperationLoggerItem;
            if (item != null)
            {
                if (item.IsQueued)
                {
                    item.IsCanceled = true;
                    item.IsQueued = false;
                }
            } 
        }
        public static void MTPDeleteSingleFileSync(this MTPStorageItem parentStorageItem, MTPStorageItem rootStorageItem, string fileName)
        {
            WMDMStorage fileItem;
            (parentStorageItem.StorageItem as IWMDMStorage2).GetStorage(fileName, out fileItem);
            if(fileItem != null)
            {
                IWMDMStorageControl3 wmdmStgCtlV3 = (IWMDMStorageControl3)fileItem;
                wmdmStgCtlV3.Delete((uint)WMDM_MODE.WMDM_CONTENT_FILE | (uint)WMDM_MODE.WMDM_MODE_BLOCK, null);
                rootStorageItem.SafeInvalidateStorageGlobal();
            }
            parentStorageItem.ChildStorages.RemoveAsync(new MTPStorageItem() { StorageName = fileName, });
        }

        public static Task MTPDeleteSingleFileAsync(this MTPStorageItem parentStorageItem, MTPStorageItem rootStorageItem, CancellationToken cancellationToken, TaskScheduler taskScheduler, MTPStorageItem fileItem)
        {
            if (parentStorageItem == null) throw new ArgumentNullException("parentStorageItem");
            OperationLoggerItem loggeritem = new OperationLoggerItem() { DisplayName = fileItem.StorageName, IsQueued = true, IsCanceled = false, OperationType = OperationType.Remove};
            loggeritem.AddLoggerItem();
            CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.Register(CancellationTokenRegisterCallback, new WeakReference(loggeritem));
            return Task.Factory.StartNew(() =>
            {
                loggeritem.IsQueued = false;
                try
                {
                    IWMDMStorageControl3 wmdmStgCtlV3 = (IWMDMStorageControl3)fileItem.StorageItem;
                    wmdmStgCtlV3.Delete((uint)WMDM_MODE.WMDM_MODE_BLOCK, null);
                    rootStorageItem.SafeInvalidateStorageGlobal();
                    parentStorageItem.ChildStorages.RemoveAsync(fileItem);
                }
                catch(IOException e)
                {
                    loggeritem.IsFailed = true;
                    loggeritem.DisplayMessage = e.Message;
                }
                catch(COMException e)
                {
                    if (e.ErrorCode == unchecked((int)WMDM_ERROR.WMDM_E_USER_CANCELLED))
                    {
                        loggeritem.IsCanceled = true;
                    }
                    else
                    {
                        loggeritem.IsFailed = true;
                        loggeritem.DisplayMessage = e.Message;
                    }
                }
                finally
                {
                    loggeritem.IsCompleted = true;
                    loggeritem.HasProgress = false;
                    cancellationTokenRegistration.Dispose();
                }
            }, cancellationToken, TaskCreationOptions.None, taskScheduler);
        }

        public static List<Task> MTPDeleteFiles(this MTPStorageItem parentStorageItem, MTPStorageItem rootStorageItem, IEnumerable<MTPStorageItem> filenames)
        {
            return filenames.Select<MTPStorageItem, Task>((item) =>
            {
                return parentStorageItem.MTPDeleteSingleFileAsync(rootStorageItem, CancellationToken.None, new CurrentThreadTaskScheduler(), item);
            }).ToList();
        }

        public static List<Task> MTPDeleteFilesAsync(this MTPStorageItem parentStorageItem, MTPStorageItem rootStorageItem, CancellationToken cancellationToken, TaskScheduler taskScheduler, IEnumerable<MTPStorageItem> filenames)
        {
            if (parentStorageItem == null) throw new ArgumentNullException("parentStorageItem");
            return filenames.Select<MTPStorageItem, Task>((item) => {
                return parentStorageItem.MTPDeleteSingleFileAsync(rootStorageItem, cancellationToken, taskScheduler, item);
            }).ToList();
        }

        public static void MTPRenameFile(this MTPStorageItem storageItem, string newItemName)
        {
            IWMDMStorageControl3 wmdmStgCtlV3 = (IWMDMStorageControl3)storageItem.StorageItem;
            wmdmStgCtlV3.Rename((uint)WMDM_MODE.WMDM_CONTENT_FILE | (uint)WMDM_MODE.WMDM_MODE_BLOCK, newItemName, null);           
        }

        public static void MTPMoveFile(this MTPStorageItem rootStorageItem, MTPStorageItem storageItem, string movetoFilePath)
        {
            IWMDMStorageControl3 wmdmStgCtlV3 = (IWMDMStorageControl3)storageItem.StorageItem;
            wmdmStgCtlV3.Move((uint)WMDM_MODE.WMDM_CONTENT_FILE | (uint)WMDM_MODE.WMDM_MODE_BLOCK, null, null);
        }

        public static void MTPCreateAsPlaylist(this MTPStorageItem rootStorageItem, IEnumerable<MTPStorageItem> storageItemList)
        {
            MTPStorageItem playlistStorage = rootStorageItem.SafeGetChildFolderStorageItem("Playlists");
            string playlistName = DateTime.Now.ToString("yyyyMMddHHmmss") + ".playlist";
            IWMDMStorage4 wmdmStorage = (IWMDMStorage4)playlistStorage.MTPInsertSingleFileSync(rootStorageItem, CancellationToken.None,  true, playlistName ).StorageItem;
            IWMDMStorage[] wmdmStorageRefs = storageItemList.Select((p) => { return (IWMDMStorage)p.StorageItem; }).ToArray();
            wmdmStorage.SetReferences(wmdmStorageRefs.Length, wmdmStorageRefs);
            new OperationLoggerItem() { DisplayName = playlistName, OperationType = OperationType.Insert, DisplayMessage = string.Join(" ;", storageItemList.Select((s) => { return s.StorageName; })) }.AddLoggerItem();
        }

        public static void MTPRemoveItemsFromPlaylist(this MTPStorageItem rootStorageItem, IEnumerable<MTPStorageItem> storageItemList)
        {
            MTPStorageItem defaultPlaylistStorageItem = rootStorageItem.SafeGetChildFolderStorageItem("Playlists").SafeGetChildFileStorageItem("default.playlist");
            IWMDMStorage4 wmdmStorage4 = (IWMDMStorage4)defaultPlaylistStorageItem.StorageItem;

            IWMDMStorage[] wmdmStorageOldRefs = null;
            int nRefs = 0;
            wmdmStorage4.GetReferences(out nRefs, out wmdmStorageOldRefs);
            new OperationLoggerItem() { DisplayName = "Old default.playlist", DisplayMessage = string.Join(" ,", wmdmStorageOldRefs.Select((s) => { return s.GetStorageName(); })) }.AddLoggerItem();

            var wmdmStorageNewRefs = wmdmStorageOldRefs.Where((s)=>{
                return !storageItemList.Select((ss) => { return ss.StorageItem; }).Contains(s, IWMDMStorageEqualityComparer<IWMDMStorage>.Default);
            }).ToArray();
            wmdmStorage4.SetReferences(wmdmStorageNewRefs.Length, wmdmStorageNewRefs);
            new OperationLoggerItem() { DisplayName = "New default.playlist", DisplayMessage = string.Join(" ,", wmdmStorageNewRefs.Select((s) => { return s.GetStorageName(); })) }.AddLoggerItem();
        }

        public static void MTPAddItemsToDefaultPlaylist(this MTPStorageItem rootStorageItem, IEnumerable<MTPStorageItem> storageItemList)
        {
            MTPStorageItem defaultPlaylistStorageItem = rootStorageItem.SafeGetChildFolderStorageItem("Playlists").SafeGetChildFileStorageItem("default.playlist");

            IWMDMStorage4 wmdmStorage4 = (IWMDMStorage4)defaultPlaylistStorageItem.StorageItem;
            IWMDMStorage[] wmdmStorageOldRefs = null;
            int nRefs = 0;
            wmdmStorage4.GetReferences(out nRefs, out wmdmStorageOldRefs);
            new OperationLoggerItem() { DisplayName = "Old default.playlist", DisplayMessage = string.Join(" ,", wmdmStorageOldRefs.Select((s) => { return s.GetStorageName(); })) }.AddLoggerItem();

            var wmdmStorageNewRefs = wmdmStorageOldRefs.Union(storageItemList.Select((s) => { return (IWMDMStorage)s.StorageItem; }), IWMDMStorageEqualityComparer<IWMDMStorage>.Default).ToArray();
            wmdmStorage4.SetReferences(wmdmStorageNewRefs.Length, wmdmStorageNewRefs);
            new OperationLoggerItem() { DisplayName = "New default.playlist", DisplayMessage = string.Join(" ,", wmdmStorageNewRefs.Select((s) => { return s.GetStorageName(); })) }.AddLoggerItem();
        }

        public static void MTPSetAsDefaultPlaylist(this MTPStorageItem rootStorageItem, MTPStorageItem currentSelected)
        {
            MTPStorageItem defaultPlaylistStorageItem = rootStorageItem.SafeGetChildFolderStorageItem("Playlists").SafeGetChildFileStorageItem("default.playlist");
            IWMDMStorage4 wmdmDefaultPlaylistStorage4 = (IWMDMStorage4)defaultPlaylistStorageItem.StorageItem;

            IWMDMStorage[] wmdmStorageOldRefs = null;
            int nRefs = 0;
            wmdmDefaultPlaylistStorage4.GetReferences(out nRefs, out wmdmStorageOldRefs);
            new OperationLoggerItem() { DisplayName = "Old default.playlist", DisplayMessage = string.Join(" ,", wmdmStorageOldRefs.Select((s) => { return s.GetStorageName(); })) }.AddLoggerItem();

            IWMDMStorage4 wmdmStorage4;
            IWMDMStorage[] wmdmStorageNewRefs = null;
            wmdmStorage4 = (IWMDMStorage4)currentSelected.StorageItem;
            wmdmStorage4.GetReferences(out nRefs, out wmdmStorageNewRefs);

            wmdmDefaultPlaylistStorage4.SetReferences(wmdmStorageNewRefs.Length, wmdmStorageNewRefs);
            new OperationLoggerItem() { DisplayName = "New default.playlist", DisplayMessage = string.Join(" ,", wmdmStorageNewRefs.Select((s) => { return s.GetStorageName(); })) }.AddLoggerItem();
        }
    }
}
